//抽象类
// 概述：抽象类是⼀种⽆法被实例化的类，专⻔⽤来定义类的结构和⾏为，类中可以写抽象
// ⽅法，也可以写具体实现。抽象类主要⽤来为其派⽣类提供⼀个基础结构，要求其派⽣类
// 必须实现其中的抽象⽅法。
//
// 简记：抽象类不能实例化，其意义是可以被继承，抽象类⾥可以有普通⽅法、也可以有抽
// 象⽅法。
//
//通过以下场景，理解抽象类：
// 我们定义⼀个抽象类 Package ，表示所有包裹的基本结构，任何包裹都有重量属性 weight
// ，包裹都需要计算运费。但不同类型的包裹（如：标准速度、特快专递）都有不同的运费计算
// ⽅式，因此⽤于计算运费的calculate ⽅法是⼀个抽象⽅法，必须由具体的⼦类来实现。
abstract class Package {
    //构造方法
    constructor(public weight: number) {

    }
    //抽象方法
    abstract calculate(): number

    //具体方法
    printPackage() {
        console.log(`包裹重量为：${this.weight}kg,运费为：${this.calculate()}`)
    }
}

//抽象类不能实例化
// const pa1 = new Package(123)

//StandardPackage 类继承了 Package ，实现了 calculate ⽅法：
class StandardPackage extends Package {
    //构造方法
    constructor(weight: number, public unitPrice: number) {
        super(weight)
    }

    // 实现抽象⽅法：计算运费
    calculate(): number {
        return this.weight * this.unitPrice
    }
}

const stand1 = new StandardPackage(10, 5)
// console.log(stand1.calculate())
stand1.printPackage()

//ExpressPackage 类继承了 Package ，实现了 calculate ⽅法：
//ExpressPackage 类（特快包裹） 
class ExpressPackage extends Package {
    //构造方法
    constructor(
        public weight: number,
        public unitPrice: number,
        public additional: number
    ) {
        super(weight)
    }

    //抽象方法实现
    calculate(): number {
        if (this.weight > 10) {
            return 10 * this.unitPrice + (this.weight - 10) * this.additional
        }
        else {
            return 10 * this.unitPrice
        }
    }
}

const expre1 = new ExpressPackage(15, 10, 12)
expre1.printPackage()

//总结：何时使⽤抽象类？
// 定义通用接口:为⼀组相关的类定义通⽤的⾏为（⽅法或属性）时。
// 提供基础实现：在抽象类中提供某些⽅法或为其提供基础实现，这样派⽣类就可以继承这些实现。
// 确保关键实现：强制派⽣类实现⼀些关键⾏为。
// 共享代码和逻辑：当多个类需要共享部分代码时，抽象类可以避免代码重复。


